#include "vl_tunnel.h"

using namespace VL;

/**
 * @Author: CimZzz
 * @Date: 2022-07-14 17:24:20
 * 
 * 构造方法, 不允许实例化, 仅支持子类继承
 */
BaseTunnel::BaseTunnel(): nextTunnel(nullptr) {
	this->lastTunnel = nullptr;
	this->inTerminator = nullptr;
	this->outTerminator = nullptr;
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-19 16:10:42
 * 
 * 析构方法, 如果存在上级管道和下级管道, 则会将其连接起来
 */
BaseTunnel::~BaseTunnel() {
	if(this->inTerminator != nullptr) {
		this->inTerminator->resetTunnel();
	}
	if(this->outTerminator != nullptr) {
		this->outTerminator->resetTunnel();
	}
}


/**
 * @Author: CimZzz
 * @Date: 2022-07-14 17:46:18
 * 
 * 向下级通道传递消息
 * 
 * @param message 通道消息指针引用
 * 
 * @note 如果下级通道不存在, 则该方法无效果
 */
void BaseTunnel::passMessage(const std::shared_ptr<TunnelMessage>& message) {
	if(this->nextTunnel == nullptr) {
		if(this->outTerminator != nullptr) {
			this->outTerminator->recvTunnelMessage(message);
		}
	} else {
		this->nextTunnel->passMessage(message);
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-14 19:35:11
 * 
 * 向上级通道回复消息
 * 
 * @param message 通道消息指针引用
 * 
 * @note 如果上级通道不存在, 则该方法无效果
 */
void BaseTunnel::replyMessage(const std::shared_ptr<TunnelMessage>& message) {
	if(this->lastTunnel == nullptr) {
		if(this->inTerminator != nullptr) {
			this->inTerminator->recvTunnelMessage(message);
		}
	} else {
		this->lastTunnel->passMessage(message);
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-15 12:02:33
 * 
 * 向下级管道传递数据
 * @param data 数据指针
 */
void BaseTunnel::passData(const std::shared_ptr<TunnelData>& data) {
	if(this->nextTunnel == nullptr) {
		if(this->outTerminator != nullptr) {
			this->outTerminator->recvTunnelData(data);
		}
	} else {
		this->nextTunnel->doPass(data);
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-15 12:02:33
 * 
 * 向上级管道回复数据
 * @param data 数据指针
 */
void BaseTunnel::replyData(const std::shared_ptr<TunnelData>& data) {
	if(this->lastTunnel == nullptr) {
		if(this->inTerminator != nullptr) {
			this->inTerminator->recvTunnelData(data);
		}
	} else {
		this->lastTunnel->doReply(data);
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-14 17:25:09
 * 
 * 传递数据方法
 * @param data 管道数据
 * 
 * @note 通过参数中的数据块获取数据进行处理, 可以选择继续向下传递或者抛出异常交由上/下线 Tunnel 处理
 */
void BaseTunnel::doPass(const std::shared_ptr<TunnelData>& data) {
	this->passData(data);
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-14 17:25:14
 * 
 * 回复数据方法
 * @param data 管道数据
 * 
 * @note 通过参数中的数据块获取数据进行处理, 可以选择继续向上回复或者抛出异常交由上/下线 Tunnel 处理
 */
void BaseTunnel::doReply(const std::shared_ptr<TunnelData>& data) {
	this->replyData(data);
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-14 17:42:15
 * 
 * 连接下一个管道
 * @param tunnel 管道指针
 * @return 返回下一级管道
 */
BaseTunnel* BaseTunnel::connect(BaseTunnel* tunnel) {
	this->nextTunnel = tunnel;
	this->nextTunnel->lastTunnel = this;
	if(tunnel->inTerminator != nullptr) {
		tunnel->inTerminator->input(this);
		tunnel->inTerminator = nullptr;
	}
	if(this->outTerminator != nullptr) {
		this->outTerminator->output(tunnel);
		this->outTerminator = nullptr;
	}
	return this->nextTunnel;
}



/**
 * @Author: CimZzz
 * @Date: 2022-07-19 17:22:58
 * 
 * 输出到指定终端
 * @param terminator 终端指针
 */
void BaseTunnel::output(Terminator* terminator) {
	terminator->output(this);
}


/**
 * @Author: CimZzz
 * @Date: 2022-07-19 17:24:46
 * 
 * 重置当前管道到独立状态
 * 
 * @attention 直接将链中该管道断开，不会重连前后管道与终端
 */
void BaseTunnel::resetAll() {
	if(this->lastTunnel != nullptr) {
		this->lastTunnel->nextTunnel = nullptr;
		this->lastTunnel = nullptr;
	}
	if(this->nextTunnel != nullptr) {
		this->nextTunnel->lastTunnel = nullptr;
		this->nextTunnel = nullptr;
	}
	if(this->inTerminator != nullptr) {
		this->inTerminator->resetTunnel();
		this->inTerminator = nullptr;
	}
	
	if(this->outTerminator != nullptr) {
		this->outTerminator->resetTunnel();
		this->outTerminator = nullptr;
	}
}

/**
 * @Author: CimZzz
 * @Date: 2022-07-19 17:24:27
 * 
 * 销毁全部管道
 * 自动回收内存, 不需要再次 delete
 */
void BaseTunnel::destroyAll() {
	BaseTunnel* lastTunnel = this->lastTunnel;
	BaseTunnel* nextTunnel = this->nextTunnel;
	this->resetAll();
	if(lastTunnel != nullptr) {
		lastTunnel->destroyAll();
	}
	if(nextTunnel != nullptr) {
		nextTunnel->destroyAll();
	}
	delete this;
}